#include <stdafx.h>
#include <omp.h>
#include <cmath>
#include <functional>
#include <iostream>
#include <sstream>
#include <iomanip>

float rect_integral(const std::function<float(float)> &fun,
                    const float a, const float b, const int n) {
  float h = fabs((b - a) / n);

  float sum = 0;
#pragma omp parallel reduction (+: sum)
{
# pragma omp for
  for (int i = 0; i < n; ++i)
    sum += fun(a + i * h) * h;
} // Ipragma omp parallel

  return sum;
}

float rect_eps_integral(const std::function<float(float)> &fun,
                    const float a, const float b, const float eps) {
  float h = fabs(b - a);
  float sum = h * fun(a);

  while (true) {
    h /= 2;

    float newSum = 0;
    for (float x = a; x < b; x += h)
      newSum += fun(x) * h;

    if (fabs(sum - newSum) < eps)
      break;
    sum = newSum;
  }

  return sum;
}

float rect_eps_integral_2(const std::function<float(float)> &fun,
                    const float a, const float b, const float eps) {
  int n = 1;
  float sum = fabs(b - a) * fun(a);
  float newSum;

  while (true) {
    n *= 2;
    float h = fabs(b - a) / n;

    newSum = 0;
#pragma omp parallel shared(h) reduction (+: newSum) num_threads(2)
{
# pragma omp for
    for (int i = 0; i < n; ++i)
      newSum += fun(i * h + a) * h;
} // pragma omp parallel

    if (fabs(sum - newSum) < eps)
      break;
    sum = newSum;
  }

  return sum;
}

int main(int argc, char *argv[]) {
  if (argc < 3) {
    std::cout << "bad arguments: [number of rectangle] [accuracy] \n";
    return -1;
  }
  int rectNum;
  float eps;

  std::stringstream sstr;
  sstr << argv[1] << " " << argv[2];
  sstr >> rectNum >> eps;

  std::cout << "rect_integral: " << std::endl
            << std::fixed << std::setprecision(10)
            << rect_integral([](float x) { return x * x; }, -1, 1, rectNum)
            << std::endl;

  std::cout << "rect_eps_integral_2: " << std::endl
            << std::fixed << std::setprecision(10)
            << rect_eps_integral_2([](float x) { return x * x; }, -1, 1, eps)
            << std::endl;
}
